#include "StdTcp.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "StdThread.h"
#include "../GlobalMessage.h"
#include "DoubleLinkList.h"
#include "DataCent.h"
#include "StdThreadPool.h"
#include "FileSystem.h"
#define ip "172.19.66.170"
#define port 8081

DLlist ClientList; //在线用户列表
FileSys *fs; //文件系统
char* GetOnlinelist()
{    
    char*content = (char*)malloc(ContentSize); //首先，分配了一段内存空间来存储在线客户端列表。
    if(content == NULL) //在分配内存时，检查是否成功分配。如果内存分配失败，则打印错误信息并退出程序。
    {
        printf("GetOnlinelist malloc error!\n");
        exit(-1);
    }
    memset(content,0,ContentSize); //将分配的内存空间清零，以确保内容为空。
     DLNode *travelPoint = ClientList.head->next; //遍历客户端链表，从链表的头节点的下一个节点开始。
    while(travelPoint != NULL) //使用循环遍历链表中的每个节点，直到遍历完整个链表。
    {
         CInfo *c = (CInfo*)travelPoint->data; //从当前节点中获取客户端信息。
         strcat(content,c->ClientName); //将当前客户端名称拼接到 content 字符串中。
         strcat(content,","); //在客户端名称后面添加逗号分隔符。
         travelPoint = travelPoint->next; //将指针移动到下一个节点，继续遍历。
    }
    content[strlen(content)-1] = '\0'; //在最后一个客户端名称后面的逗号替换为字符串结束符 \0。
    return content; //返回存储了在线客户端列表的字符串。
}

void SendFileToClient(MSG*m,int ClientSock,const *fileName,const char * UserDirPath)
{            
    //   printf("fileName : %s\n",fileName); 
    char  filePath [1024] = {0};
    sprintf(filePath,"%s/%s",UserDirPath,fileName);
    PackFile *pf = PacketFileInit(filePath);
    printf("filePath: %s\n",filePath);
    memcpy(m->Content + 4,fileName,sizeof(fileName));
    printf("fileName in message: %s\n",m->Content + 4);
    int size = 1000;
    //   int count = 0;
    while(IsFileEnd(pf) == false)
    {
        ReadFromFile(pf,m->Content + 24,&size);
        //填入读取的文件内容大小
        memcpy(m->Content,&size,sizeof(int));
        printf("content:%s\n",m->Content);

        TcpServerSend(ClientSock,m,sizeof(MSG));
        //     count++;
        //     printf("这是第%d包数据，大小%d\n",count,size);
        //   
    }
    size =-1 ;
    memcpy(m->Content,&size,sizeof(int));
    TcpServerSend(ClientSock,m,sizeof(MSG));
    ClearPacketFile(pf);
}
/// @brief 
/// @param m 
/// @param clientSock 
void SolveRequest(MSG*m,int clientSock)
{
    switch(m->flag) //根据消息中的标志来判断客户端的请求类型。
    {
        case SIGNUP: //如果是登录请求，将客户端信息插入到客户端链表中，并打印当前在线客户端列表。
            //当前用户名未被注册
            if(IsUserExist(m->FromName) == false)
            {
            if(AddNewUser(m->FromName,m->Content) == true)
              {
                    InsertDLlistTial(&ClientList,CreateCInfo(m->FromName,clientSock)); //将新创建的客户端信息节点插入到客户端链表的尾部。这个节点包含了从消息结构体中获取的客户端名称和套接字信息。
                    TravelDLlist(&ClientList,PrintCInfo); //遍历客户端链表，并打印每个客户端的信息。这个函数会调用 PrintCInfo 函数来打印客户端信息。
                    memset(m->Content,0,ContentSize);
                    strcpy(m->Content,"注册成功");
                    CreateNewDirInFs(fs,m->FromName);
              }
              else
              {
                    memset(m->Content,0,ContentSize);
                    strcpy(m->Content,"服务器错误");
              }
            }
            //用户名已被注册
            else
            {
                  memset(m->Content,ContentSize,0);
                 strcpy(m->Content,"用户名已被注册");

            }
            TcpServerSend(clientSock,m,sizeof(MSG));
            break;
         //登录
        case LOGIN:
             if(IsUserExist(m->FromName) == true)
             {
               if(IsAccountEqual(m->FromName,m->Content) == true)
               {
                 InsertDLlistTial(&ClientList,CreateCInfo(m->FromName,clientSock)); //将新创建的客户端信息节点插入到客户端链表的尾部。这个节点包含了从消息结构体中获取的客户端名称和套接字信息。
                 TravelDLlist(&ClientList,PrintCInfo); 
                 memset(m->Content,0,ContentSize);
                 strcpy(m->Content,"success");
               }
               else
               {
                
                 memset(m->Content,0,ContentSize);
                 strcpy(m->Content,"密码不匹配");

               }
             }
             else
             {
                memset(m->Content,0,ContentSize);
                 strcpy(m->Content,"账号不存在");
             }
             TcpServerSend(clientSock,m,sizeof(MSG));
             break;
        case CHAT: //如果是聊天请求，查找目标客户端是否在线，如果在线则向目标客户端发送消息。
        case SENDFILE:
           DLlist findList = FindByElement(&ClientList,m->ToName,IsClientNameEqual); //通过调用 FindByElement 函数，在客户端链表中查找名称与消息中目标名称匹配的客户端信息节点。这里使用了 IsClientNameEqual 函数来判断节点的名称是否与目标名称相等。
           if(findList.len == 0) //如果未找到匹配的客户端信息节点，则打印一条消息说明未找到目标用户。
           {
               printf("没有找到名称为|%s|的用户\n",m->ToName);
           }
           else
           {
               CInfo *c = (CInfo*)findList.head->next->data; //将该节点的数据转换为 CInfo 结构体指针 c
               printf("准备给|%s||套接字:%d|的用户发消息\n",c->ClientName,c->sock); //打印一条消息，说明准备向该客户端发送消息，包括客户端名称和套接字信息。
               TcpServerSend(c->sock,m,sizeof(MSG)); //向指定客户端的套接字发送消息。c->sock：目标客户端的套接字，用于发送消息m：消息结构体的指针，包含待发送的消息内容sizeof(MSG)：消息结构体的大小，用于确定发送的消息长度。
           }
           ClearDLlist(&findList,NULL); //清空临时链表 findList 中的节点。这里传入了 NULL 参数，表示不需要释放节点中的数据，因为数据是指向原始链表中的节点的。
           break;
       case BROADCAST: //如果是广播请求，遍历客户端链表并向每个在线客户端发送消息，但不包括发送者本身。
          DLNode *travelPoint = ClientList.head->next; //初始化一个指针 travelPoint，指向客户端链表的第一个节点的下一个节点，即跳过头节点。
          while(travelPoint != NULL) //进入循环，条件是 travelPoint 不为空。
          {
               CInfo *c = (CInfo*)travelPoint->data; //在循环中，首先将当前节点的数据转换为 CInfo 结构体指针 c。
               if(strcmp(c->ClientName,m->FromName) != 0) //检查当前客户端的名称是否与消息中的发送者名称不相等
               {
                   TcpServerSend(c->sock,m,sizeof(MSG)); //如果不相等，说明当前客户端不是消息的发送者，那么就向该客户端发送消息。
               }
               travelPoint = travelPoint->next; //将指针移动到下一个节点，继续遍历链表。
          } 
        break;

    case ONLINELIST: //如果是请求获取在线列表，调用 GetOnlinelist 函数获取在线列表字符串，然后将列表发送给客户端。
         char*content = GetOnlinelist(); //调用 GetOnlinelist 函数获取在线列表字符串。
         printf("在线列表为:%s\n",content); //打印在线列表。
         strcpy(m->Content,content); //将在线列表拷贝到消息结构体中的内容字段。
         TcpServerSend(clientSock,m,sizeof(MSG)); //将消息发送给客户端。
         free(content); //释放在线列表字符串所占用的内存空间。
         break;

    case UPLOAD:
    {
        int size = 0;
        memcpy(&size,m->Content,4);
        if(size == -1)
        {   
            memset(m->Content,0,sizeof(m->Content));
            strcpy(m->Content,"OK");
            TcpServerSend(clientSock,m,sizeof(MSG));
            break;
        }
        char fileName[NameSize] = {0};
        strcpy(fileName,m->Content + 4);

        WriteFileToFs(fs,m->FromName,fileName,m->Content + 24,size);
        break;
    }
    case FILELIST:
    {
        char *list = GetFileList(fs,m->FromName);
        printf("文件列表:%s\n",list);
        if(list == NULL)
        {
            memset(m->Content,0,sizeof(m->Content));
            strcpy(m->Content,"FileError");
        }
        else
        {
            memset(m->Content,0,sizeof(m->Content));
            strcpy(m->Content,list);
            free(list);
        }
        TcpServerSend(clientSock,m,sizeof(MSG));
        break;
    }
    
    case DOWNLOAD:
    {   
        printf("%s %d %s\n", m->FromName, m->flag, m->Content);
        char filepath[1024] = {0};
        sprintf(filepath,"%s/%s", "/home/259/chatroom/filesystem", m->FromName);
        SendFileToClient(m, clientSock,m->Content,filepath);
        break;
    }
    }
    }



void *ClientHandle(void*arg) //定义了一个函数 ClientHandle，用于处理客户端的消息接收和响应
{   
    int clientSock = *((int *)arg); //将参数 arg 解引用为整型，得到客户端套接字 clientSock
    while(1)
    {   
        MSG m; //在循环内部，先声明一个消息结构体 m，用于存储接收到的消息内容
       if(TcpServerRecv(clientSock,&m,sizeof(MSG)) == false ) //调用 TcpServerRecv 函数接收客户端发送的消息，如果接收失败（返回 false），则打印客户端下线的信息并跳出循环
       {
           printf("客户端|%d|已下线\n",clientSock);
           break;
        
       }
        // printf("收到来自客户端|%d|的消息：%s\n",clientSock,msg);
       SolveRequest(&m,clientSock); //调用 SolveRequest 函数来处理接收到的消息，并传入消息结构体 m 和客户端套接字 clientSock。
    }
    //删除用户
    RemoveByElement(&ClientList,(void*)clientSock,IsClientSockEqual,free); //当循环结束时，表示客户端已经下线，将其从客户端链表中移除，然后返回 NULL
    return NULL;
}
int main()
{   
    if(DataCenterInit() == false)
    {
        printf("数据中心初始化失败！\n");
        return -1;
    }
    InitDLlist(&ClientList); //初始化客户端链表 ClientList。
    TcpS *s = TcpServerInit(ip,port); //初始化 TCP 服务器，监听指定 IP 地址和端口
    ThreadP *p = ThreadPoolInit(10,2,10,3600);
    fs = FileSystemInit("./filesystem");
    int clientSock = -1; //声明了一个整型变量 clientSock，并将其初始化为 -1。通常这种做法是为了确保在程序开始时该变量有一个明确的初始值，表示当前没有有效的客户端套接字与之关联。在后续的代码中，clientSock 的值会根据实际情况被赋予不同的套接字值
    printf("等待客户端连接...\n");
    while ((clientSock = TcpServerAccept(s)) >= 0) //进入一个循环，不断接受新的客户端连接
    {
        printf("接入新的客户端，套接字是%d\n",clientSock); 
        ThreadPoolAddTask (p,ClientHandle,&clientSock);
    }
    
    
   return 0;

}